﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;

namespace KvDbApi
{
    public sealed class BufferedValueCollection : IEnumerable<SqlDataRecord>
    {
        private readonly byte[] inputBuffer;

        public BufferedValueCollection(byte[] buffer)
            : this(buffer, 0, buffer.Length)
        {
        }

        public BufferedValueCollection(byte[] buffer, int offset, int max)
        {
            Usage.ValidateForBufferOperation(buffer, offset, max);
            this.inputBuffer = buffer;
            this.Offset = offset;
            this.Max = max;
        }

        internal int Offset { get; set; }

        internal int Max { get; set; }

        public IEnumerator<SqlDataRecord> GetEnumerator()
        {
            return new RecordEnumerator(this.inputBuffer, this.Offset, this.Max);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        private class RecordEnumerator : IEnumerator<SqlDataRecord>
        {
            private readonly SqlDataRecord record;

            private readonly SqlBytes sqlBuffer;

            private readonly int max;

            private readonly byte[] value;

            [ThreadStatic]
            private static byte[] localBuffer;

            private bool started;

            private int sortOrder;

            private int bufferOffset;

            public RecordEnumerator(byte[] buffer, int offset, int maxLength)
            {
                if (localBuffer == null)
                {
                    localBuffer = new byte[Usage.ValuePartSize];
                }

                this.record = CreateValueRecord(Usage.ValuePartSize);
                this.sqlBuffer = new SqlBytes(localBuffer);
                this.value = buffer;
                this.max = maxLength;
                this.bufferOffset = offset;
            }

            public SqlDataRecord Current
            {
                get
                {
                    if (!this.started)
                    {
                        throw new InvalidOperationException("The enumerator is not started. Call MoveNext() first.");
                    }

                    return this.record;
                }
            }

            object IEnumerator.Current
            {
                get
                {
                    return this.Current;
                }
            }

            public void Dispose()
            {
            }

            public bool MoveNext()
            {
                this.started = true;
                var nextCount = Math.Min(this.max - this.bufferOffset, Usage.ValuePartSize);
                if (nextCount < 1)
                {
                    return false;
                }

                this.sqlBuffer.Write(0, this.value, this.bufferOffset, nextCount);
                this.sqlBuffer.SetLength(nextCount);
                this.record.SetInt32(0, this.sortOrder++);
                this.record.SetSqlBytes(1, this.sqlBuffer);
                this.bufferOffset += nextCount;
                return true;
            }

            public void Reset()
            {
                this.sortOrder = 0;
                this.bufferOffset = 0;
                this.started = false;
            }

            private static SqlDataRecord CreateValueRecord(int size)
            {
                return new SqlDataRecord(
                        new SqlMetaData("SortOrder", SqlDbType.Int),
                        new SqlMetaData("Part", SqlDbType.VarBinary, size));
            }
        }
    }
}